package com.me.study.socket.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

public class NioServer {

    public static void main(String[] args) throws IOException {
        // 创建 Selector
        Selector selector = Selector.open();

        // 创建 ServerSocketChannel 并绑定端口
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false);

        // 将 SocketChannel 注册到 Selector上，并监听 ACCEPT 事件
        // 通常先注册一个 OP_ACCEPT 事件, 然后在 OP_ACCEPT 到来时, 再将这个 Channel 的 OP_READ
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 阻塞等待就绪的事件
            selector.select();

            // 获取就绪的SelectionKey集合
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // 表示该 IO 事件已处理，需要删除
                iterator.remove();
                if (!key.isValid()) continue;

                // 处理ACCEPT事件，key.channel() 返回的 ServerSocketChannel
                if (key.isAcceptable()) {
                    accept(key, selector);
                } else if (key.isReadable()) {
                    // 有 READ事件，获取的为 SocketChannel
                    read(key);
                }
                else if (key.isWritable()) {
                    write(key);
                }
            }
        }
    }

    private static void read(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        StringBuilder sb = new StringBuilder();

        // TODO -1 表示客户端断开连接
        // TODO 0 表示当前没有数据可用
        int size;
        while ((size = clientChannel.read(buffer)) > 0) {
            buffer.flip();

            // 使用CharsetDecoder解码字节数据
            CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
            CharBuffer charBuffer = decoder.decode(buffer);

            // 将解码后的字符追加到StringBuilder中
            sb.append(charBuffer);

            // 移除已读取的字节，并切换为写模式
            buffer.compact();
        }
        buffer.flip(); // 切换为读模式
        while (buffer.hasRemaining()) {
            CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
            CharBuffer charBuffer = decoder.decode(buffer);
            sb.append(charBuffer);
        }

        System.out.println("Received: " + sb);
        // 小于 0 表示客户端断开连接，需要关闭该 SocketChannel
        if (size < 0) {
            clientChannel.close();
        }
    }

    public static void write(SelectionKey key) throws IOException {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        int bufferSize = 1024;
        ByteBuffer buffer = ByteBuffer.allocate(bufferSize);

        // 填充数据
        byte[] bytes = "hello, world!".getBytes();
        for (int start = 0; start < bytes.length; start += bufferSize) {
            // 剩余数据的长度
            int remain = bytes.length - start;
            buffer.put(bytes, start, Math.min(remain, bufferSize));
            buffer.flip();
            socketChannel.write(buffer);
            buffer.clear();
        }
    }

    private static void accept(SelectionKey key, Selector selector) throws IOException {
        // TODO key.channel 需要 try-resource 吗？
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        SocketChannel clientChannel = serverChannel.accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(selector, SelectionKey.OP_READ);
    }

}
